﻿// === Copyright 2008 Jamie Macia et al. ===
// Module:      DiceClasses
// Created:     11/18/2008
// Author:      Jamie Macia
// Description: Library that provides types related to the rolling of "dice" in the context of d20 gaming
// Revisions:
//      *jmacia 11/18 Issue 1: Created

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace JMacia.Gaming.d20
{
    /// <summary>
    /// This struct represents a set of polyhedral dice.
    /// </summary>
    public struct DiceSet
    {
        #region Private Members

        private int _number;
        private int _sides;
        
        #endregion

        #region Constructors

        /// <summary>
        /// Constructor for a single-die set
        /// </summary>
        /// <param name="sides">The number of sides on the die</param>
        public DiceSet(int sides)
        {
            _sides = sides;
            _number = 1;
        }

        /// <summary>
        /// Constructor for any number of dice
        /// </summary>
        /// <param name="number">The number of dice in the set</param>
        /// <param name="sides">The number of sides on each die</param>
        public DiceSet(int number, int sides)
        {
            _sides = sides;
            _number = number;
        }
        
        #endregion

        #region Public Properties

        /// <summary>
        /// The number of dice in the set
        /// </summary>
        public int Number
        {
            get
            {
                return _number;
            }
            set
            {
                _number = value;
            }
        }

        /// <summary>
        /// The number of sides on each die
        /// </summary>
        public int Sides
        {
            get
            {
                return _sides;
            }
            set
            {
                _sides = value;
            }
        }
        
        #endregion

        #region Public Methods

        /// <summary>
        /// Represents the set of dice as a string
        /// </summary>
        /// <returns>"XdY", where X is the number of dice and Y is the number of sides per die</returns>
        /// <example>Two six-sided dice (DiceSet(2,6)) would be represented as "2d6"</example>
        public override string ToString()
        {
            return Number.ToString() + "d" + Sides.ToString();
        }

        /// <summary>
        /// Quickly rolls a single set of dice.
        /// </summary>
        /// <returns>The total value of all the dice rolled</returns>
        public int Roll()
        {
            DiceRoll roll = new DiceRoll(this);
            return roll.Roll();
        }
        
        #endregion
    }

    /// <summary>
    /// A base class to represent and roll an arbitrary number of dice plus or minus an integral modifier
    /// </summary>
    public class DiceRoll
    {
        #region Protected Members

        protected List<DiceSet> _dice = new List<DiceSet>();
        protected static Random _rand = new Random();
        protected int _modifier;
        
        #endregion

        #region Constructors

        /// <summary>
        /// Empty constructor.
        /// </summary>
        public DiceRoll() { }

        /// <summary>
        /// Constructs a DiceRoll from a properly formatted string
        /// </summary>
        /// <param name="rollDefinition">The string to be parsed</param>
        /// <seealso cref="Parse(string)"/>
        public DiceRoll(string rollDefinition)
        {
            Parse(rollDefinition);
        }

        /// <summary>
        /// Constructs a DiceRoll from one or more DiceSets
        /// </summary>
        /// <param name="sets">One or more sets of dice to be rolled</param>
        public DiceRoll(params DiceSet[] sets)
        {
            AddDice(sets);
        }

        /// <summary>
        /// Constructs a DiceRoll from one or more DiceSets and an integral modifier
        /// </summary>
        /// <param name="modifier">The modifier to apply to the total roll</param>
        /// <param name="sets">One or more sets of dice to be rolled</param>
        public DiceRoll(int modifier, params DiceSet[] sets)
        {
            _modifier = modifier;
            AddDice(sets);
        }

        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="copy">The DiceRoll of which to make a copy</param>
        public DiceRoll(DiceRoll copy)
        {
            this._dice = new List<DiceSet>(copy._dice);
            this._modifier = copy._modifier;
        }
        
        #endregion

        #region Public Properties

        /// <summary>
        /// Parses a string 
        /// </summary>
        /// <param name="rollDefinition">
        /// A string containing the expression to be rolled.
        /// This should be of the form "UdV+XdY+...+Z", 
        /// where U-Y are positive integers and Z is an integer.  
        /// Spaces are ignored.</param>
        /// <exception cref="ArgumentException">Thrown if a number within the string cannot be parsed</exception>
        /// <seealso cref="int.Parse(string)"/>
        public virtual void Parse(string rollDefinition)
        {
            StringBuilder builder = new StringBuilder(rollDefinition);
            builder.Replace("-", "+-");
            builder.Replace(" ", string.Empty);

            string[] pieces = builder.ToString().Split('+');
            foreach (string piece in pieces)
            {
                if (string.IsNullOrEmpty(piece))
                {
                    continue;
                }

                int sides, numDice, pos = piece.IndexOf("d", StringComparison.InvariantCultureIgnoreCase);
                if (pos > 0)
                {
                    try
                    {
                        numDice = int.Parse(piece.Substring(0, pos));
                        sides = int.Parse(piece.Substring(pos + 1));
                    }
                    catch (Exception e)
                    {
                        throw new ArgumentException("Could not parse the dice to be rolled: " + piece, e);
                    }

                    AddDice(numDice, sides);
                }
                else if (pos == 1)
                {
                    numDice = 1;
                }
                else
                {
                    try
                    {
                        this._modifier += int.Parse(piece);
                    }
                    catch (Exception e)
                    {
                        throw new ArgumentException("Could not parse the modifier: " + piece, e);
                    }
                }
            }
        }

        /// <summary>
        /// Adds dice to the roll.
        /// </summary>
        /// <param name="numDice">Number of dice to be added</param>
        /// <param name="dieSides">Number of sides per die to be added</param>
        public virtual void AddDice(int numDice, int dieSides)
        {
            _dice.Add(new DiceSet(numDice, dieSides));
        }

        /// <summary>
        /// Adds sets of dice to the roll.
        /// </summary>
        /// <param name="sets">Sets of dice to be added</param>
        public virtual void AddDice(params DiceSet[] sets)
        {
            _dice.AddRange(sets);
        }

        /// <summary>
        /// Rolls the dice.
        /// </summary>
        /// <returns>The total value of the dice and the modifier</returns>
        public virtual int Roll()
        {
            List<int> dummy;
            return Roll(out dummy);
        }

        /// <summary>
        /// Rolls the dice.
        /// </summary>
        /// <param name="individualRolls">
        /// A list to be populated with the values of the individual rolls</param>
        /// <returns>The total value of the dice and the modifier</returns>
        public virtual int Roll(out List<int> individualRolls)
        {
            individualRolls = new List<int>();
            int total = _modifier;
            foreach (DiceSet set in _dice)
            {
                for (int i = 0; i < set.Number; i++)
                {
                    int roll = _rand.Next(1, set.Sides + 1);
                    total += roll;
                    individualRolls.Add(roll);
                }
            }
            return total;
        }

        /// <summary>
        /// Represents the group of dice as a string.
        /// </summary>
        /// <returns>
        /// A string representing the dice to be rolled.
        /// This will be of the form "UdV+XdY+...+Z", 
        /// where U-Y are positive integers and Z is an integer.</returns>
        public override string ToString()
        {
            StringBuilder build = new StringBuilder();
            for (int i = 0; i < _dice.Count; i++)
            {
                build.Append(_dice[i].ToString());
                if (i < _dice.Count - 1)
                {
                    build.Append(" + ");
                }
            }

            if (_modifier < 0)
            {
                build.Append(" - ");
                build.Append(-_modifier);
            }
            else if (_modifier > 0)
            {
                build.Append(" + ");
                build.Append(_modifier);
            }

            return build.ToString();
        }
        
        #endregion
    }

    /// <summary>
    /// An extension of the DiceRoll class specific to damage.
    /// This class takes into account the fact that some damage is multiplied on a critical hit while some is not.
    /// </summary>
    public class DamageRoll : DiceRoll
    {
        #region Protected Members

        protected int _critMultiplier = 2;
        protected DiceRoll _staticDamage = new DiceRoll();
        
        #endregion

        #region Constructors

        /// <summary>
        /// Empty constructor
        /// </summary>
        public DamageRoll() : base() { }

        /// <summary>
        /// Constructs a DamageRoll (all multiplicable) from a properly formatted string
        /// </summary>
        /// <param name="rollDefinition">A string that defines the multipliable damage</param>
        public DamageRoll(string rollDefinition) : base(rollDefinition) { }

        /// <summary>
        /// Constructor that defines both multipliable and static damage from strings
        /// </summary>
        /// <param name="multipliableDmg">The damage that is multiplied on a critical hit</param>
        /// <param name="staticDmg">The damage that is not multiplied on a critical hit</param>
        /// <seealso cref="DiceRoll.Parse(string)"/>
        public DamageRoll(string multipliableDmg, string staticDmg)
            : base(multipliableDmg)
        {
            _staticDamage.Parse(staticDmg);
        }

        /// <summary>
        /// Constructs the damage roll from two DiceRolls
        /// </summary>
        /// <param name="multipliableDmg">The damage that is multiplied on a critical hit</param>
        /// <param name="staticDmg">The damage that is not multiplied on a critical hit</param>
        public DamageRoll(DiceRoll multipliableDmg, DiceRoll staticDmg)
            : base(multipliableDmg)
        {
            _staticDamage = new DiceRoll(staticDmg);
        }

        /// <summary>
        /// Allows user to specify a critical hit multiplier
        /// </summary>
        /// <param name="multipliableDmg">The damage that is multiplied on a critical hit</param>
        /// <param name="staticDmg">The damage that is not multiplied on a critical hit</param>
        /// <param name="critMultiplier">The number of times the multipliable damage is rolled on a critical hit</param>
        public DamageRoll(string multipliableDmg, string staticDmg, int critMultiplier)
            : base(multipliableDmg)
        {
            _staticDamage.Parse(staticDmg);
            _critMultiplier = critMultiplier;
        }

        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="copy">The DamageRoll of which to make a copy</param>
        public DamageRoll(DamageRoll copy)
            : base(copy)
        {
            this._critMultiplier = copy._critMultiplier;
            this._staticDamage = new DiceRoll(copy._staticDamage);
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Parses both the multipliable and static damage
        /// </summary>
        /// <param name="multipliableRollDefinition">The damage that is multiplied on a critical hit</param>
        /// <param name="staticRollDefinition">The damage that is not multiplied on a critical hit</param>
        public void Parse(string multipliableRollDefinition, string staticRollDefinition)
        {
            base.Parse(multipliableRollDefinition);
            _staticDamage.Parse(staticRollDefinition);
        }

        /// <summary>
        /// Parses the static damage only.
        /// </summary>
        /// <param name="staticRollDefinition">The damage that is not multiplied on a critical hit</param>
        public void ParseStatic(string staticRollDefinition)
        {
            _staticDamage.Parse(staticRollDefinition);
        }

        /// <summary>
        /// Rolls both the multipliable and the static damage.
        /// </summary>
        /// <param name="individualRolls">
        /// A list to be populated with the values of the individual rolls</param>
        /// <returns>The total value of the dice and the modifier</returns>
        /// <remarks cref="DiceRoll.Roll()">
        /// This will be called polymorphically by Roll()</remarks>
        public override int Roll(out List<int> individualRolls)
        {
            List<int> stat;
            int val = base.Roll(out individualRolls);
            val += _staticDamage.Roll(out stat);
            individualRolls.AddRange(stat);
            return val;
        }

        /// <summary>
        /// Adds dice to the static damage.
        /// </summary>
        /// <param name="numDice">Number of dice to be added</param>
        /// <param name="dieSides">Number of sides per die to be added</param>
        public void AddStaticDice(int numDice, int dieSides)
        {
            _staticDamage.AddDice(numDice, dieSides);
        }

        /// <summary>
        /// Adds sets of dice to the static damage roll
        /// </summary>
        /// <param name="sets">Sets of dice to be added</param>
        public void AddStaticDice(params DiceSet[] sets)
        {
            _staticDamage.AddDice(sets);
        }

        /// <summary>
        /// Rolls a critical hit
        /// </summary>
        /// <returns>The total damage</returns>
        public virtual int RollCritical()
        {
            int roll = _staticDamage.Roll();
            for (int i = 0; i < _critMultiplier; i++)
            {
                roll += base.Roll();
            }
            return roll;
        }

        /// <summary>
        /// Rolls a critical hit
        /// </summary>
        /// <param name="individualRolls">The individual damage dice</param>
        /// <returns>The total damage</returns>
        public virtual int RollCritical(out List<int> individualRolls)
        {
            List<int> statRolls;
            int roll = _staticDamage.Roll(out statRolls);
            individualRolls = new List<int>();
            for (int i = 0; i < _critMultiplier; i++)
            {
                List<int> rolls;
                roll += base.Roll(out rolls);
                individualRolls.AddRange(rolls);
            }
            individualRolls.AddRange(statRolls);
            return roll;
        }

        /// <summary>
        /// String representation of all the damage dice
        /// </summary>
        /// <returns>a string representation of the damage</returns>
        public override string ToString()
        {
            return string.Format("{0}+{1}", base.ToString(), _staticDamage);
        }

        /// <summary>
        /// An overload of the ToString method that allows you to specify what is included in the output
        /// </summary>
        /// <param name="type">
        /// A string representing the type of output desired.
        /// "M" indicates multipliable damage only,
        /// "S" indicates static damage only,
        /// "A" or anything else indicates all damage.
        /// It is case insensitive and discards all but the first character</param>
        /// <returns>The damage string requested</returns>
        public string ToString(string type)
        {
            if (type.Length == 0)
            {
                return ToString();
            }
            switch (type.ToUpper()[0])
            {
                case 'M':   // multipliable damage
                    return base.ToString();
                case 'S':   // static damage
                    return _staticDamage.ToString();
                case 'A':   // all damage
                default:    // unsupported, default to all
                    return ToString();
            }
        }
        
        #endregion

        #region Public Properties

        /// <summary>
        /// The critical hit multiplier.  Multipliable damage is rolled this many times when rolling a critical hit.
        /// </summary>
        public int CritMultiplier
        {
            get
            {
                return _critMultiplier;
            }
            set
            {
                _critMultiplier = value;
            }
        }

        /// <summary>
        /// A string representation of the multipliable damage.  Used for XML serialization.
        /// </summary>
        public string SerialMultipliableDamage
        {
            get
            {
                return base.ToString();
            }
            set
            {
                Parse(value);
            }
        }

        /// <summary>
        /// A string representation of the static damage.  Used for XML serialization.
        /// </summary>
        public string SerialStaticDamage
        {
            get
            {
                return _staticDamage.ToString();
            }
            set
            {
                _staticDamage.Parse(value);
            }
        }
        
        #endregion
    }

    /// <summary>
    /// An extension of the DiceRoll class to represent attack rolls
    /// </summary>
    public class AttackRoll : DiceRoll
    {
        #region Protected Members
        protected int _iterativeAttacks = 0;
        protected int _attacksAtHighest = 1;
        protected int _criticalThreshold = 20;
        #endregion        

        #region Constructors

        /// <summary>
        /// Empty constructor
        /// </summary>
        public AttackRoll() : base() { }

        /// <summary>
        /// Constructs an attack roll from an attack bonus
        /// </summary>
        /// <param name="attackBonus">The highest attack bonus</param>
        public AttackRoll(int attackBonus) : base(attackBonus, new DiceSet(1, 20)) { }

        /// <summary>
        /// Constructs a full attack roll
        /// </summary>
        /// <param name="attackBonus">The highest attack bonus in the attack sequence</param>
        /// <param name="attacksAtHighest">The number of attacks at the highest bonus</param>
        /// <param name="iterativeAttacks">The number of iterative attacks at a cumulative -5 penalty per attack</param>
        public AttackRoll(int attackBonus, int attacksAtHighest, int iterativeAttacks)
            : base(attackBonus, new DiceSet(1, 20))
        {
            _attacksAtHighest = attacksAtHighest;
            _iterativeAttacks = iterativeAttacks;
        }

        /// <summary>
        /// Constructs a full attack roll including critical hit threshold
        /// </summary>
        /// <param name="attackBonus">The highest attack bonus in the attack sequence</param>
        /// <param name="attacksAtHighest">The number of attacks at the highest bonus</param>
        /// <param name="iterativeAttacks">The number of iterative attacks at a cumulative -5 penalty per attack</param>
        /// <param name="criticalThreshold">The lowest die roll that threatens a critical hit</param>
        public AttackRoll(int attackBonus, int attacksAtHighest, int iterativeAttacks, int criticalThreshold)
            : base(attackBonus, new DiceSet(1, 20))
        {
            _attacksAtHighest = attacksAtHighest;
            _iterativeAttacks = iterativeAttacks;
            _criticalThreshold = criticalThreshold;
        }

        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="copy">The AttackRoll of which to make a copy</param>
        public AttackRoll(AttackRoll copy)
            : base(copy)
        {
            this._iterativeAttacks = copy._iterativeAttacks;
            this._attacksAtHighest = copy._attacksAtHighest;
            this._criticalThreshold = copy._criticalThreshold;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// The highest attack bonus in the full attack sequence
        /// </summary>
        public int HighestBonus
        {
            get
            {
                return _modifier;
            }
            set
            {
                _modifier = value;
            }
        }

        /// <summary>
        /// The number of attacks in a full attack sequence at the 
        /// highest attack bonus
        /// </summary>
        /// <seealso cref="HighestBonus"/>
        public int AttacksAtHighest
        {
            get
            {
                return _attacksAtHighest;
            }
            set
            {
                _attacksAtHighest = value;
            }
        }

        /// <summary>
        /// The number of iterative attacks in the full attack sequence, 
        /// each at a cumulative -5 penalty from the highest bonus
        /// </summary>
        public int IterativeAttacks
        {
            get
            {
                return _iterativeAttacks;
            }
            set
            {
                _iterativeAttacks = value;
            }
        }

        /// <summary>
        /// The lowest die roll necessary to threaten a critical hit
        /// </summary>
        public int CriticalHitThreshold
        {
            get
            {
                return _criticalThreshold;
            }
            set
            {
                _criticalThreshold = value;
            }
        }
        
        #endregion

        #region Public Methods and Support Functions

        /// <summary>
        /// Parses a string into a full attack sequence
        /// </summary>
        /// <param name="rollDefinition">
        /// A string of the form X[/X][/...][/X-5][/X-10][...] where X is an integer.
        /// (Brackets denote an optional piece, not a literal bracket)</param>
        /// <exception cref="ArgumentException">Thrown if rollDefinition is not formatted as above</exception>
        public override void Parse(string rollDefinition)
        {
            rollDefinition.Replace(" ", string.Empty);
            string[] attacks = rollDefinition.Split('/');

            if (int.TryParse(attacks[0], out _modifier))
            {
                _iterativeAttacks = 0;
                _attacksAtHighest = 0;
                int currentMod = _modifier;
                for (int i = 1; i < attacks.Length; i++)
                {
                    int now;
                    if (!int.TryParse(attacks[i], out now))
                    {
                        StringBuilder message = new StringBuilder("Parse of attack roll modifier ");
                        message.Append(i + 1);
                        message.Append(" in the attack routine \"");
                        message.Append(rollDefinition);
                        message.Append("\" failed.  Attack modifiers must be valid integers delimited by forward slashes (/)");

                        throw new ArgumentException(message.ToString());
                    }

                    if (now == _modifier)
                    {
                        _attacksAtHighest++;
                    }
                    else
                    {
                        currentMod -= 5;
                        if (now == currentMod)
                        {
                            _iterativeAttacks++;
                        }
                        else
                        {
                            StringBuilder message = new StringBuilder("Illegitimate attack routine: ");
                            message.Append(rollDefinition);
                            message.Append(". Attack routines must be of the form X[/X][...][/X-5][X-10][...]");

                            throw new ArgumentException(message.ToString());
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Represents the full attack pattern in a string
        /// </summary>
        /// <returns>Sequence of integer attack bonuses delimited by forward slashes (/)</returns>
        public override string ToString()
        {
            StringBuilder build = new StringBuilder();
            int mod = _modifier;
            for (int i = 0; i < _attacksAtHighest; i++)
            {
                build.Append(mod);
                if (i < _attacksAtHighest - 1)
                {
                    build.Append('/');
                }
            }
            for (int i = 0; i < _iterativeAttacks; i++)
            {
                build.Append('/');
                mod -= 5;
                build.Append(mod);
            }

            return build.ToString();
        }

        /// <summary>
        /// Inner method to resolve attack rolls, including critical hit confirmation
        /// </summary>
        /// <param name="armorClass">
        /// The armor class of the attack's target</param>
        /// <param name="confirmCrit">
        /// True if this call is to confirm a critical hit,
        /// False otherwise</param>
        /// <returns>An AttackResult containing the result of the attack</returns>
        protected virtual AttackResult Roll(int armorClass, bool confirmCrit)
        {
            AttackResult result = new AttackResult();
            List<int> dummy;
            result.ModifiedRoll = Roll(out dummy);
            result.UnmodifiedRoll = dummy[0];

            if (result.UnmodifiedRoll == 20 || (result.ModifiedRoll >= armorClass && result.UnmodifiedRoll != 1))
            {
                result.Result = AttackResultEnum.Hit;
                if (!confirmCrit && result.UnmodifiedRoll >= _criticalThreshold)
                {
                    result.ParseConfirmation(Roll(armorClass, true));  //roll to confirm
                }//if we're not confirming a crit, and this attack threatens a crit
            }//if we roll a natural 20, or we hit the AC outright without rolling a 1
            else
            {
                result.Result = AttackResultEnum.Miss;
            }//else, we missed

            return result;
        }

        /// <summary>
        /// Rolls a single attack at the highest attack bonus
        /// </summary>
        /// <param name="againstAC">The armor class of the attack's target</param>
        /// <returns>An AttackResult denoting the specifics of the attack roll</returns>
        public AttackResult Roll(int againstAC)
        {
            return Roll(againstAC, false);
        }

        /// <summary>
        /// Rolls the entire full attack sequence
        /// </summary>
        /// <param name="armorClass">The armor class of the attacks' target</param>
        /// <returns>A System.List of AttackResults 
        /// containing the specifics of all the attacks</returns>
        public List<AttackResult> RollFullAttack(int armorClass)
        {
            List<AttackResult> vals = new List<AttackResult>();
            int originalModifier = _modifier;
            for (int i = 0; i < _attacksAtHighest; i++)
            {
                vals.Add(Roll(armorClass));
            }
            for (int i = 0; i < _iterativeAttacks; i++)
            {
                _modifier -= 5;
                vals.Add(Roll(armorClass));
            }
            _modifier = originalModifier;

            return vals;
        } 

        #endregion
    }

    /// <summary>
    /// A structure to contain all the specific of an attack roll against an AC
    /// </summary>
    public struct AttackResult
    {
        #region Private Members

        private AttackResultEnum _result;
        private int _modified;
        private int _unmodified;
        private int _confirmModified;
        private int _confirmUnmodified;
        private bool _critThreat;
        
        #endregion

        #region Constructors

        /// <summary>
        /// Constructor for an attack that did not threaten a critical hit
        /// </summary>
        /// <param name="result">Result of the attack (miss, hit, crit)</param>
        /// <param name="modified">The attack roll with attack bonus</param>
        /// <param name="unmodified">The unmodified die roll</param>
        public AttackResult(AttackResultEnum result, int modified, int unmodified)
        {
            _result = result;
            _modified = modified;
            _unmodified = unmodified;
            _confirmModified = 0;
            _confirmUnmodified = 0;
            _critThreat = false;
        }

        /// <summary>
        /// Constructor for an attack that may have threatened a critical hit
        /// </summary>
        /// <param name="result">Result of the attack (miss, hit, crit)</param>
        /// <param name="modified">The attack roll with attack bonus</param>
        /// <param name="unmodified">The unmodified die roll</param>
        /// <param name="confirmModified">The critical hit confirmation roll with attack bonus</param>
        /// <param name="confirmUnmodified">The unmodified die roll for the confirmation</param>
        public AttackResult(AttackResultEnum result, int modified, int unmodified, int confirmModified, int confirmUnmodified)
        {
            _result = result;
            _modified = modified;
            _unmodified = unmodified;
            _confirmModified = confirmModified;
            _confirmUnmodified = confirmUnmodified;
            _critThreat = (confirmUnmodified > 0);
        }

        /// <summary>
        /// Constructor for an attack that definitely threatened a critical hit
        /// </summary>
        /// <param name="modified">The attack roll with attack bonus</param>
        /// <param name="unmodified">The unmodified die roll</param>
        /// <param name="critResult">Result of the confirmation roll</param>
        public AttackResult(int modified, int unmodified, AttackResult critResult)
        {
            _modified = modified;
            _unmodified = unmodified;
            _confirmModified = critResult._modified;
            _confirmUnmodified = critResult._unmodified;
            _critThreat = true;
            if (critResult._result == AttackResultEnum.Hit)
            {
                _result = AttackResultEnum.CriticalHit;
            }
            else
            {
                _result = AttackResultEnum.Hit;
            }
        } 

        #endregion

        #region Public Methods

        /// <summary>
        /// Changes the attack result given the result of the critical confirmation roll
        /// </summary>
        /// <param name="critResult">Result of the confirmation roll</param>
        public void ParseConfirmation(AttackResult critResult)
        {
            _confirmModified = critResult._modified;
            _confirmUnmodified = critResult._unmodified;
            _critThreat = true;
            if (critResult._result == AttackResultEnum.Hit)
            {
                _result = AttackResultEnum.CriticalHit;
            }
            else
            {
                _result = AttackResultEnum.Hit;
            }
        }
        
        #endregion

        #region Public Properties

        /// <summary>
        /// The tri-state attack result (miss/hit/crit)
        /// </summary>
        public AttackResultEnum Result
        {
            get
            {
                return _result;
            }
            set
            {
                _result = value;
            }
        }

        /// <summary>
        /// The attack roll modified by the attack bonus
        /// </summary>
        public int ModifiedRoll
        {
            get
            {
                return _modified;
            }
            set
            {
                _modified = value;
            }
        }

        /// <summary>
        /// The attack's die roll without any modifier
        /// </summary>
        public int UnmodifiedRoll
        {
            get
            {
                return _unmodified;
            }
            set
            {
                _unmodified = value;
            }
        }

        /// <summary>
        /// The critical hit confirmation roll, modified by the attack bonus
        /// </summary>
        public int CritConfirmModifiedRoll
        {
            get
            {
                return _confirmModified;
            }
            set
            {
                _confirmModified = value;
            }
        }

        /// <summary>
        /// The critical hit confirmation die roll without any modifiers
        /// </summary>
        public int CritConfirmUnmodifiedRoll
        {
            get
            {
                return _confirmUnmodified;
            }
            set
            {
                _confirmUnmodified = value;
            }
        }

        /// <summary>
        /// True if this attack threatened a critical hit, false otherwise
        /// </summary>
        public bool ThreatensCritical
        {
            get
            {
                return _critThreat;
            }
            set
            {
                _critThreat = value;
            }
        }
        
        #endregion
    }

    /// <summary>
    /// Tri-state enumeration indicating the result of an attack
    /// </summary>
    public enum AttackResultEnum
    {
        /// <summary>
        /// The attack missed
        /// </summary>
        Miss,
        /// <summary>
        /// The attack scored a hit, but not a critical hit
        /// </summary>
        Hit,
        /// <summary>
        /// The attack scored a critical hit
        /// </summary>
        CriticalHit
    }
}
